package com.winning.nio;

import com.winning.utils.IOUtils;
import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

/**
 * 一、通道（Channel）：用于源节点与目标节点的连接。在 Java NIO 中负责缓冲区中数据的传输。Channel 本身不存储数据，因此需要配合缓冲区进行传输。
 *
 * 二、通道的主要实现类
 * 	java.nio.channels.Channel 接口：
 * 		|--FileChannel
 * 		|--SocketChannel
 * 		|--ServerSocketChannel
 * 		|--DatagramChannel
 *
 * 三、获取通道
 * 1. Java 针对支持通道的类提供了 getChannel() 方法
 * 		本地 IO：
 * 		FileInputStream/FileOutputStream
 * 		RandomAccessFile
 *
 * 		网络IO：
 * 		Socket
 * 		ServerSocket
 * 		DatagramSocket
 *
 * 2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
 * 3. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()
 *
 * 四、通道之间的数据传输
 * transferFrom()
 * transferTo()
 *
 * 五、分散(Scatter)与聚集(Gather)
 * 分散读取（Scattering Reads）：将通道中的数据分散到多个缓冲区中
 * 聚集写入（Gathering Writes）：将多个缓冲区中的数据聚集到通道中
 *
 * 六、字符集：Charset
 * 编码：字符串 -> 字节数组
 * 解码：字节数组  -> 字符串
 *
 *
 * i. FileChannel.MapMode.READ_ONLY：得到的镜像只能读不能写（只能使用get之类的读取Buffer中的内容）；
 * ii. FileChannel.MapMode.READ_WRITE：得到的镜像可读可写（既然可写了必然可读），对其写会直接更改到存储节点；
 * iii. FileChannel.MapMode.PRIVATE：得到一个私有的镜像，其实就是一个(position, size)区域的副本罢了，也是可读可写，只不过写不会影响到存储节点，就是一个普通的ByteBuffer了
 *
 * StandardOpenOption
 * READ	               以读取方式打开文件
 * WRITE　　	           已写入方式打开文件
 * CREATE	           如果文件不存在，创建
 * CREATE_NEW	      如果文件不存在，创建；若存在，异常。
 * APPEND	          在文件的尾部追加
 * DELETE_ON_CLOSE	  当流关闭的时候删除文件
 * TRUNCATE_EXISTING  把文件设置为0字节
 * SPARSE	          文件不够时创建新的文件
 * SYNC	              同步文件的内容和元数据信息随着底层存储设备
 * DSYNC              同步文件的内容随着底层存储设备
 */
public class ChannelTest {

    @Test
    public void test2() throws IOException {
        long start = System.currentTimeMillis();

        FileChannel inChannel = FileChannel.open(Paths.get("F:/迅雷下载/1.avi"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("F:/迅雷下载/2.avi"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);

        MappedByteBuffer inMappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outMappedBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

        byte[] dst = new byte[inMappedBuf.limit()];
        inMappedBuf.get(dst);
        outMappedBuf.put(dst);

        inChannel.close();
        outChannel.close();

        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

    @Test
    public void test1(){
        long start = System.currentTimeMillis();

        FileInputStream fis = null;
        FileOutputStream fos = null;
        //获取通道
        FileChannel inChannel = null;
        FileChannel outChannel = null;

        try{
            fis = new FileInputStream("F:/迅雷下载/1.avi");
            fos = new FileOutputStream("F:/迅雷下载/2.avi");

            inChannel = fis.getChannel();
            outChannel = fos.getChannel();

            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            while (inChannel.read(byteBuffer) != -1){
                byteBuffer.flip();
                outChannel.write(byteBuffer);
                byteBuffer.clear();
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {

            IOUtils.close(fis);
            IOUtils.close(fos);
            IOUtils.close(inChannel);
            IOUtils.close(outChannel);

        }
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }
}
